
function xywh2xyxy(bbox) {
	var x1 = Math.max(0, bbox[0] - Math.round(bbox[2] / 2));
	var y1 = Math.max(0, bbox[1] - Math.round(bbox[3] / 2));
	var x2 = bbox[0] + Math.round(bbox[2] / 2);
	var y2 = bbox[1] + Math.round(bbox[3] / 2);
	ret = [x1, y1, x2, y2];

	return ret;
}

function cod_crf(out_info, h_pre, w_pre, h_after, w_after) {
	var inp_w = w_pre;
	var inp_h = h_pre;
	var w_after = w_after;
	var h_after = h_after;
	var bboxs = out_info['pred_bbox'];
	var cls = out_info['cls'];
	var scores = out_info['scores'];
	var output = {};

	if (bboxs.length == 0) return output;

	var scale = Math.max(inp_w / w_after, inp_h / h_after);
	var h_pre = inp_h / scale;
	var w_pre = inp_w / scale;
	var x_move = Math.abs(w_pre - w_after) / 2;
	var y_move = Math.abs(h_pre - h_after) / 2;

	var x1 = bboxs.map(function (t) {
		return Math.max(0, t[0] - Math.round(t[2] / 2));
	});
	var y1 = bboxs.map(function (t) {
		return Math.max(0, t[1] - Math.round(t[3] / 2));
	});
	var x2 = bboxs.map(function (t) {
		return t[0] + Math.round(t[2] / 2);
	});
	var y2 = bboxs.map(function (t) {
		return t[1] + Math.round(t[3] / 2);
	});

	for (var i = 0; i < bboxs.length; i++) {
		var ret_x1 = (x1[i] - x_move) * scale;
		var ret_x2 = (x2[i] - x_move) * scale;
		var ret_y1 = (y1[i] - y_move) * scale;
		var ret_y2 = (y2[i] - y_move) * scale;
		bboxs[i] = [ret_x1, ret_y1, ret_x2, ret_y2];
	}
	output['pred_bbox'] = bboxs;
	output['scores'] = scores;
	output['cls'] = cls;

	return output;
}

function nms(dets, conf_thres, iou_thres) {
	var pred_bbox = dets['pred_bbox'];
	var scores = dets['pred_conf'] || [];
	var pred_class = dets['pred_class'] || [];
	var out_info = {};
	var cls = [];
	var indices = [];

	for (var i = 0; i < scores.length; i++) {
		if (scores[i] > conf_thres) {   // 0.5
			indices.push(i);
		}
	}
	console.log('indices', indices.length);

	pred_bbox = indices.map(idx => pred_bbox[idx]);
	scores = indices.map(idx => scores[idx]);
	pred_class = indices.map(idx => pred_class[idx]);
	for (var i = 0; i < pred_class.length; i++) {
		var maxIndex = pred_class[i].indexOf(Math.max(...pred_class[i]));
		cls.push(maxIndex);
	}
	console.log('cls', cls, scores);
	var keep = [];
	var totall_cls = Array.from(new Set(cls));
	for (var j = 0; j < totall_cls.length; ++j) {
		var keep_index = [];
		var cuttent_cls = totall_cls[j];
		for (var i = 0; i < cls.length; ++i) {
			if (cls[i] === cuttent_cls) {
				keep_index.push(i);
			}
		}

		var cur_pred_bbox = keep_index.map(idx => pred_bbox[idx]);
		var current_scores = keep_index.map(idx => scores[idx]);

		if (keep_index.length === 1) {
			var x1 = Math.max(0, cur_pred_bbox[0] - Math.round(cur_pred_bbox[2] / 2));
			var y1 = Math.max(0, cur_pred_bbox[1] - Math.round(cur_pred_bbox[3] / 2));
			var x2 = cur_pred_bbox[0] + Math.round(cur_pred_bbox[2] / 2);
			var y2 = cur_pred_bbox[1] + Math.round(cur_pred_bbox[3] / 2);
		} else {
			var x1 = cur_pred_bbox.map(function (t) {
				return Math.max(0, t[0] - Math.round(t[2] / 2));
			});
			var y1 = cur_pred_bbox.map(function (t) {
				return Math.max(0, t[1] - Math.round(t[3] / 2));
			});
			var x2 = cur_pred_bbox.map(function (t) {
				return t[0] + Math.round(t[2] / 2);
			});
			var y2 = cur_pred_bbox.map(function (t) {
				return t[1] + Math.round(t[3] / 2);
			});
		}

		var areas = [];
		for (var i = 0; i < cur_pred_bbox.length; ++i) {
			var area = (x2[i] - x1[i] + 1) * (y2[i] - y1[i] + 1);
			areas.push(area);
		}

		var order = keep_index.map(function (t, i) {
			return i;
		}).sort(function (a, b) {
			return current_scores[b] - current_scores[a];
		});
		while (order.length > 0) {
			var idx = order.shift();
			keep.push(keep_index[idx]);

			for (var i = 0; i < order.length; i++) {
				var other_idx = order[i];
				var xx1 = Math.max(x1[idx], x1[other_idx]);
				var yy1 = Math.max(y1[idx], y1[other_idx]);
				var xx2 = Math.min(x2[idx], x2[other_idx]);
				var yy2 = Math.min(y2[idx], y2[other_idx]);

				var w = Math.max(0, xx2 - xx1);
				var h = Math.max(0, yy2 - yy1);
				var inter_area = w * h;
				var iou = inter_area / (areas[idx] + areas[other_idx] - inter_area);

				if (iou > iou_thres) {
					order.splice(i, 1);
					i--;
				}
			}
		}
	}

	pred_bbox = keep.map(idx => pred_bbox[idx]);
	scores = keep.map(idx => scores[idx]);
	cls = keep.map(idx => cls[idx]);

	out_info['index'] = keep;
	out_info['pred_bbox'] = pred_bbox;
	out_info['scores'] = scores;
	out_info['cls'] = cls;

	return out_info;
}

function std_output(pred) {
	const numCols = pred[0].length;
	const numRow = pred.length;
	const pred_bbox = [];
	const pred_class = [];
	const pred_conf = [];
	const mergedPredictions = {};

	for (let i = 0; i < numRow; i++) {
		const lastCols = pred[i].slice(4 - numCols);
		const befCols = pred[i].slice(0, 4);
		pred_bbox.push(befCols);
		pred_class.push(lastCols);
	}

	pred_class.forEach(row => {
		const maxVal = Math.max(...row);
		pred_conf.push(maxVal);
	});

	mergedPredictions['pred_bbox'] = pred_bbox;
	mergedPredictions['pred_conf'] = pred_conf;
	mergedPredictions['pred_class'] = pred_class;

	return mergedPredictions;
}

function transpose(matrix) {
	const rows = matrix.length;
	if (rows === 0) return [];

	const firstRow = matrix[0];
	const cols = firstRow.length;
	const transposed = new Array(cols);

	for (let i = 0; i < cols; i++) {
		transposed[i] = new Array(rows);
	}

	for (let i = 0; i < rows; i++) {
		for (let j = 0; j < cols; j++) {
			transposed[j][i] = matrix[i][j];
		}
	}

	return transposed;
}

function arrayBufferTo2DArray(buffer, numRows, numCols) {
	// Step 1: 将 ArrayBuffer 转换为 Float32Array
	const floatArray = new Float32Array(buffer);

	// Step 2: 确保数据长度匹配
	if (floatArray.length !== numRows * numCols) {
		throw new Error('数据长度不匹配！请检查 ArrayBuffer 的内容。');
	}

	// Step 3: 将 Float32Array 转换为二维数组
	const twoDArray = [];
	console.log('floatArray', floatArray, Array.from(floatArray.slice(0, 1000)));
	for (let i = 0; i < numRows; i++) {
		const row = floatArray.slice(i * numCols, (i + 1) * numCols);
		twoDArray.push(Array.from(row));
	}

	return twoDArray;
}

export const readOutputData = (output_data) => {
	var h_pre = 640; // 原图像高宽 //
	var w_pre = 640;

	var h_after = 640; // 进模型图像高宽 //
	var w_after = 640;

	// 假设你有一个 ArrayBuffer 数据
	const arrayBuffer = output_data /* 你的 ArrayBuffer 数据 */;
	const numRows = 8400;  // 行数
	const numCols = 11;  // 列数

	// 将 ArrayBuffer 转换为二维数组
	const pred = arrayBufferTo2DArray(arrayBuffer, numRows, numCols);

	// 读取json数据：这部分数据已将（1, 11, 8400）转成（11, 8400）后保存成json文件 //

	// """ 后处理 """ // 
	const transposedPred = pred;// 二维数组 // 转置：将读取到的json数据（11行，8400列）转置成8400行，11列的列表 //
	console.log('转换为二维数组', pred);
	const mergedPredictions = std_output(transposedPred); // 将（8400， 11）处理成（8400， 12）  12 = box:4  conf:1  cls:7 //
	console.log('mergedPredictions-pred_bbox', mergedPredictions.pred_bbox);
	console.log('mergedPredictions-pred_class', mergedPredictions.pred_class);
	console.log('mergedPredictions-pred_conf', mergedPredictions.pred_conf);
	const out_info = nms(mergedPredictions, 0.5, 0.4); // 非极大值抑制NMS
	console.log('out_info', out_info);
	const output = cod_crf(out_info, h_pre, w_pre, h_after, w_after); // 因为预测框是在经过letterbox后的图像上做预测所以需要将预测框的坐标映射回原图像上 //
	console.log('后处理结果', output);
	return output;
};
